package main

import (
	"bufio"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"io"
	"log"
	"net"
	"os"
	"strings"
	"time"
)

func main() {
	listen, err := net.Listen("tcp", "127.0.0.1:20000") //监听
	if err != nil {
		log.Println(err)
		return
	}
	defer listen.Close() //结束监听
	go manager()         //创建管理者go程，用于管理用户在线列表和监听消息列表
	for {
		conn, err := listen.Accept() //建立连接
		if err != nil {
			log.Println(err)
			continue
		}
		go process(conn) //处理客户端数据请求
	}
}

type client struct {
	c    chan string
	name string
	addr string
}

var (
	Group     = make(map[string]client)                //在线的用户
	msgSystem = make(chan string)                      //系统消息
	OnlineNum = 0                                      //在线人数
	destFile  = "E:/代码文件/GO/src/chat_room/client/File" //文件的接收地址
)

func process(conn net.Conn) {
	defer conn.Close()
	addr := conn.RemoteAddr().String() //获取用户网络地址ip+port
	reader := bufio.NewReader(conn)
	var buf [1024]byte
	n, err := reader.Read(buf[:])
	if err != nil {
		log.Println(err)
		return
	}
	name1 := string(buf[:n])
	user := client{
		c:    make(chan string),
		addr: addr,
		name: name1,
	}
	go whiteMsgToUser(user, conn)
	Group[name1] = user //将新连接的用户添加到在线列表中
	msgSystem <- makeMessage(user, "已上线")
	OnlineNum++
	msgSystem <- fmt.Sprintf("\t\t\t[系统消息]在线人数为：%d", OnlineNum)
	exit := make(chan bool) //用户退出记录
	model := 0
	friend := client{}
	go func() { //处理用户之间的消息传送
		for {
			n, err = reader.Read(buf[:])
			if err != nil {
				log.Println(err)
				break
			}
			msg := strings.Trim(string(buf[:n]), "\r\n")
			if msg == "\\q" {
				exit <- true
				OnlineNum--
				msgSystem <- makeMessage(user, "已下线")
				msgSystem <- fmt.Sprintf("\t\t\t[系统消息]在线人数为：%d", OnlineNum)
				return
			}
			if msg == "@" {
				n, err = reader.Read(buf[:])
				if err != nil {
					log.Println(err)
					return
				}
				u := strings.Trim(string(buf[:n]), "\r\n")
				if u == "@all" {
					model = 0
					user.c <- "转换成功。。"
				} else if toUser, ok := Group[u]; ok {
					model = 1
					friend = toUser
					user.c <- "转换成功。。"
				} else {
					user.c <- "转换失败。。"
				}
				continue
			}
			if msg == "\\file" {
				n, err = reader.Read(buf[:])
				if err != nil {
					log.Println(err)
					return
				}
				file := strings.Trim(string(buf[:n]), "\r\n")
				if isFile(file) && sendFile(file) {
					user.c <- "文件传输成功！"
					msg = "收到来自 " + user.name + " 的文件"
				} else {
					user.c <- "该文件不存在或文件名格式不正确，文件传输失败。。"
					continue
				}
			}
			if model == 0 { //群聊
				broadcast(name1, msg)
			} else if model == 1 { //私聊
				friend.c <- msg
			}
		}
	}()

	for {
		select {
		case <-exit:
			delete(Group, user.addr) //将用户从在线列表中移除
			return
		}
	}
}

func manager() {
	//监听消息列表
	for true {
		msg := <-msgSystem
		for _, u := range Group {
			u.c <- msg //将系统消息发送给所有在线用户
		}
	}
}

func whiteMsgToUser(u client, conn net.Conn) {
	for msg := range u.c {
		conn.Write([]byte(msg))
	}
}

func makeMessage(u client, condition string) string {
	t := time.Now().Format("2006年01月02日15:04:05")
	return "\t\t\t[系统消息][" + t + "]	" + u.name + condition
}

func broadcast(name, message string) {
	for n, u := range Group {
		if n == name {
			continue
		}
		u.c <- message
	}
}

func isFile(file string) bool {
	_, err := os.Stat(file)
	if err == nil || os.IsExist(err) {
		return true
	}
	return false
}

func sendFile(srcFile string) bool {
	file1, err := os.Open(srcFile)
	if err != nil {
		log.Println(err)
		return false
	}
	i := strings.LastIndex(srcFile, "/")
	name := string(srcFile[i:])
	file2, err := os.OpenFile(destFile+name, os.O_CREATE|os.O_WRONLY, os.ModePerm)
	if err != nil {
		log.Println(err)
		return false
	}
	io.Copy(file2, file1)
	return true
}
